/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <string.h>
#include <mutex>
#include <memory>
#include <iostream>
#include <sstream>
#include <iomanip>
#include "kh_sle_host.h"
#include "sle_host_interface.h"
#include "sle_hilog.h"

#ifdef __cplusplus
extern "C" {
#endif

namespace OHOS {
namespace NearLink {
namespace SleStandard {
#define SLE_MAX_HOST_CALLBACK_NUM  10

static SleHost *g_sleHost;
static SleHostCallbacks *g_sleHostCallback[SLE_MAX_HOST_CALLBACK_NUM] = {nullptr};
static std::mutex g_hostCbLock;
static bool g_hostServiceInited = false;

class SleHostCallbackWapper : public SleHostCallback {
public:
    void OnSleStateChange(SleDeviceState state) override
    {
        SLE_LOGD("OnSleStateChange state(%s)", SleDeviceStateStr(state));
        std::lock_guard<std::mutex> lock(g_hostCbLock);
        for (uint8_t i = 0; i < SLE_MAX_HOST_CALLBACK_NUM; i++) {
            if (g_sleHostCallback[i] != nullptr && g_sleHostCallback[i]->OnSleHostStateChangeCb != nullptr) {
                g_sleHostCallback[i]->OnSleHostStateChangeCb(state);
            }
        }
    }

    void OnFlowMonitorEvent(float flow) override
    {
        std::lock_guard<std::mutex> lock(g_hostCbLock);
        for (uint8_t i = 0; i < SLE_MAX_HOST_CALLBACK_NUM; i++) {
            if (g_sleHostCallback[i] != nullptr && g_sleHostCallback[i]->OnSleFlowMonitorEventCb != nullptr) {
                g_sleHostCallback[i]->OnSleFlowMonitorEventCb(flow);
            }
        }
    }
};

static std::shared_ptr<SleHostCallbackWapper> g_HostCallbackWapper;

SleErrorCode InitSleHostService(void)
{
    SLE_LOGI("InitSleHostService");
    SLE_ASSERT_AND_RETURN_RET_LOG(g_hostServiceInited == false, SLE_ERR_STATUS_WRONG, "host service already inited error!");
    if (g_sleHost == nullptr) {
        g_sleHost = &SleHost::GetDefaultHost();
    }
    g_HostCallbackWapper = std::make_shared<SleHostCallbackWapper>();
    SLE_ASSERT_AND_RETURN_RET_LOG(g_HostCallbackWapper, SLE_ERR_FAILED, "callback wapper error!");
    SleErrorCode ret = g_sleHost->RegisterHostCallback(g_HostCallbackWapper);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "init sle host callback error");
    g_hostServiceInited = true;
    return ret;
}

SleErrorCode DeinitSleHostService(void)
{
    SLE_LOGI("DeinitSleHostService");
    SLE_ASSERT_AND_RETURN_RET_LOG(g_hostServiceInited == true, SLE_ERR_INTERNAL_WRONG, "host service not inited error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(g_HostCallbackWapper, SLE_ERR_INTERNAL_WRONG, "callback wapper error!");
    SleErrorCode ret = g_sleHost->UnregisterHostCallback(g_HostCallbackWapper);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "deinit host service failed!");
    g_hostServiceInited = false;
    return ret;
}

SleErrorCode CheckHostOperation(void)
{
    SLE_ASSERT_AND_RETURN_RET_LOG(g_hostServiceInited == true, SLE_ERR_INTERNAL_WRONG, "host service not inited");
    SLE_ASSERT_AND_RETURN_RET_LOG(g_sleHost, SLE_ERR_INTERNAL_WRONG, "host instance null");
    return SLE_NO_ERROR;
}

SleErrorCode SleEnable()
{
    SLE_LOGI("SleEnable");
    SleErrorCode ret = CheckHostOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = g_sleHost->EnableSle();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "sle enable failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleDisable()
{
    SLE_LOGI("SleDisable");
    SleErrorCode ret = CheckHostOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = g_sleHost->DisableSle();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "sle disable failed!");
    return SLE_NO_ERROR;
}

//para back
SleErrorCode GetSleState(SleDeviceState* sleHostState)
{
    SLE_LOGI("GetSleState");
    SleErrorCode ret = CheckHostOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = g_sleHost->GetSleState(*sleHostState);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "get sle state failed!");
    return SLE_NO_ERROR;
}

SleErrorCode GetHostAddress(SleDeviceAddress *hostAddr)
{
    SLE_LOGI("GetHostAddress");
    SLE_ASSERT_AND_RETURN_RET_LOG(hostAddr, SLE_ERR_INVALID_PARAM, "host address param error!");
    SleErrorCode ret = CheckHostOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = g_sleHost->GetHostAddress(*hostAddr);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "get host address failed");
    return SLE_NO_ERROR;
}

SleErrorCode SetHostName(const uint8_t *name, uint8_t len)
{
    SLE_LOGI("SetHostName");
    SLE_ASSERT_AND_RETURN_RET_LOG((len > 0 && len <= SLE_DEVICE_NAME_LEN), SLE_ERR_INVALID_PARAM, "name length param error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(name, SLE_ERR_INVALID_PARAM, "name param null error!");
    SleErrorCode ret = CheckHostOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    std::string localName(reinterpret_cast<const char *>(name), len);
    ret = g_sleHost->SetHostName(localName);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "set local name failed");
    return SLE_NO_ERROR;
}

SleErrorCode GetHostName(uint8_t *name, uint8_t *len)
{
    SLE_LOGI("GetHostName");
    SLE_ASSERT_AND_RETURN_RET_LOG(name, SLE_ERR_INVALID_PARAM, "name param null error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(len, SLE_ERR_INVALID_PARAM, "length param null error!");
    SleErrorCode ret = CheckHostOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    std::string localName;
    ret = g_sleHost->GetHostName(localName);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "get local name failed");
    if (*len > localName.length()) {
        *len = localName.length();
    }
    for (int i = 0; i < *len; i++) {
        name[i] = localName[i];
    }
    return SLE_NO_ERROR;
}

SleErrorCode GetHostDefaultConnectConfig(SleConnectConfig *hostConnParam)
{
    SLE_LOGI("GetHostDefaultConnectConfig");
    SLE_ASSERT_AND_RETURN_RET(hostConnParam != NULL, SLE_ERR_INVALID_PARAM);
    SleErrorCode ret = CheckHostOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    SleConnectConfig hostConnCfg;
    ret = g_sleHost->GetHostDefaultConnectConfig(hostConnCfg);
    memcpy(hostConnParam, &hostConnCfg, sizeof(SleConnectConfig));
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    return SLE_NO_ERROR;
}

SleErrorCode RegisterHostCallbacks(SleHostCallbacks *hostCallback)
{
    SLE_LOGI("RegisterHostCallbacks");
    SLE_ASSERT_AND_RETURN_RET(hostCallback, SLE_ERR_INVALID_PARAM);
    SleErrorCode ret = CheckHostOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    for(int i = 0; i < SLE_MAX_HOST_CALLBACK_NUM; i++) {
        SLE_ASSERT_AND_RETURN_RET_LOG(g_sleHostCallback[i] != hostCallback, SLE_NO_ERROR, "hostCallback(%p) is already registered", hostCallback);
    }
    ret = SLE_ERR_NO_MEM;
    for(int i = 0; i < SLE_MAX_HOST_CALLBACK_NUM; i++) {
        if (g_sleHostCallback[i] == nullptr) {
            g_sleHostCallback[i] = hostCallback;
            ret = SLE_NO_ERROR;
            break;
        }
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "register host callback failed(%{public}d)!", ret);
    return SLE_NO_ERROR;
}

SleErrorCode UnregisterHostCallbacks(SleHostCallbacks *hostCallback)
{
    SLE_LOGI("UnregisterHostCallbacks");
    SLE_ASSERT_AND_RETURN_RET(hostCallback, SLE_ERR_INVALID_PARAM);
    SleErrorCode ret = CheckHostOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = SLE_ERR_FAILED;
    for(int i = 0; i < SLE_MAX_HOST_CALLBACK_NUM; i++) {
        if (g_sleHostCallback[i] == hostCallback) {
            g_sleHostCallback[i] = nullptr;
            ret = SLE_NO_ERROR;
            break;
        }
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_NO_ERROR, "callback(%p) has not been registered", hostCallback);
    return ret;
}
} // SleStandard
} // NearLink
} // OHOS

#ifdef __cplusplus
}
#endif